/*
 * Copyright (c) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.huawei.health.ecology.fa.central.operator;

import com.huawei.health.ecology.fa.central.module.CentralClientModule;
import com.huawei.health.ecology.fa.central.processor.AudioPlayerProcessor;
import com.huawei.health.ecology.fa.central.request.AudioPlayRequest;
import com.huawei.health.ecology.fa.central.request.PermissionAcquireRequest;
import com.huawei.health.ecology.fa.central.request.PermissionVerifyRequest;
import com.huawei.health.ecology.fa.central.response.CentralResponseCode;
import com.huawei.healthecology.data.platform.PlatformOperation;
import com.huawei.healthecology.data.platform.PlatformRequest;
import com.huawei.healthecology.data.platform.PlatformResponse;
import com.huawei.healthecology.operator.JsRemoteOperator;

import java.util.Arrays;
import java.util.Optional;

/**
 * The Platform operator code, include Permission, Audio and Ability operations.
 */
public enum CentralOperationCode {
    CHECK_PERMISSION(2_001) {
        @Override
        Optional<PlatformResponse> toProcessRequest(PlatformOperation operation) {
            return CentralClientModule.getOhosPermissionProcessor()
                .map(processor -> operation.getRawRequest()
                    .flatMap(rawRequest -> CentralClientModule.getParser()
                        .flatMap(parser -> parser.mapTo(rawRequest, PermissionVerifyRequest.class))
                        .map(processor::checkPermissions)
                        .map(response -> PlatformResponse.builder()
                            .responseValue(response)
                            .build()))
                    .orElse(PlatformResponse.builder()
                        .responseCode(CentralResponseCode.OPERATION_FAILED)
                        .build()));
        }
    },
    ON_PERMISSION_REQUEST(2_002) {
        @Override
        Optional<PlatformResponse> toProcessRequest(PlatformOperation operation) {
            CentralClientModule.getOhosPermissionProcessor()
                .ifPresent(processor -> processor.onPermissionRequest(requestResult ->
                    CentralClientModule.getParser()
                        .flatMap(parser -> parser.stringify(requestResult))
                        .ifPresent(result -> operation.getCallbackOperator().onResponse(result))));
            return Optional.of(PlatformResponse.builder()
                .responseCode(CentralResponseCode.OPERATION_SUCCESS)
                .build());
        }
    },
    REQUEST_PERMISSION(2_003) {
        @Override
        Optional<PlatformResponse> toProcessRequest(PlatformOperation operation) {
            return CentralClientModule.getOhosPermissionProcessor()
                .map(processor -> operation.getRawRequest()
                    .flatMap(rawRequest -> CentralClientModule.getParser()
                        .flatMap(parser -> parser.mapTo(rawRequest, PermissionAcquireRequest.class))
                        .map(processor::requestPermissions)
                        .map(responseCode -> PlatformResponse.builder()
                            .responseCode(responseCode)
                            .build()))
                    .orElse(PlatformResponse.builder()
                        .responseCode(CentralResponseCode.OPERATION_FAILED)
                        .build()));
        }
    },
    START_AUDIO_PLAY(2_007) {
        @Override
        Optional<PlatformResponse> toProcessRequest(PlatformOperation operation) {
            return CentralClientModule.getAudioPlayerProcessor()
                .map(processor -> operation.getRawRequest()
                    .flatMap(rawRequest -> CentralClientModule.getParser()
                        .flatMap(parser -> parser.mapTo(rawRequest, AudioPlayRequest.class)))
                    .map(processor::playAudioList)
                    .map(responseCode -> PlatformResponse.builder()
                        .responseCode(responseCode)
                        .build())
                    .orElse(PlatformResponse.builder()
                        .responseCode(CentralResponseCode.OPERATION_FAILED)
                        .build()));
        }
    },
    STOP_AUDIO_PLAY(2_008) {
        @Override
        Optional<PlatformResponse> toProcessRequest(PlatformOperation operation) {
            return CentralClientModule.getAudioPlayerProcessor()
                .map(AudioPlayerProcessor::stopAudioPlay)
                .map(responseCode -> PlatformResponse.builder()
                    .responseCode(responseCode)
                    .build());
        }
    },
    UNKNOWN_OPERATOR(1_1111) {
        @Override
        Optional<PlatformResponse> toProcessRequest(PlatformOperation operation) {
            return Optional.empty();
        }
    };

    private final int operationValue;

    CentralOperationCode(int value) {
        this.operationValue = value;
    }

    /**
     * To process the request from Js FA platform
     *
     * @param remoteOperator The remote operator to reply Js FA response
     * @param request The platform request
     * @return true if such request submit success, false otherwise
     */
    public static boolean processJsFaOperation(JsRemoteOperator remoteOperator, PlatformRequest request) {
        return Arrays.stream(values())
            .filter(operator -> (operator.operationValue == request.getRequestCode()))
            .findAny()
            .flatMap(operator -> operator.toProcessRequest(
                PlatformOperation.builder()
                    .callbackOperator(remoteOperator)
                    .platformRequest(request)
                    .build()))
            .map(remoteOperator::writeResponse)
            .orElse(false);
    }

    abstract Optional<PlatformResponse> toProcessRequest(PlatformOperation operation);
}
